import Watcher from './watcher'

export default class Complier {
	constructor(vm) {
		this.el = vm.$el
		this.vm = vm
		this.compile(this.el)
	}

	compile(el) {
		const childNodes = el.childNodes
		Array.from(childNodes).forEach(node => {
			if (this.isTextNode(node)) {
				this.compileText(node)
			} else if (this.isElementNode(node)) {
				this.compileElement(node)
			}

			if (node.childNodes && node.childNodes.length > 0) {
				this.compile(node)
			}
		})
	}

	compileText(el) {
		let reg = /\{\{\s+(.+?)\s+\}\}/
		const value = el.textContent
		if (reg.test(value)) {
			let key = RegExp.$1.trim()
			el.textContent = value.replace(reg, this.vm[key])

			new Watcher(this.vm, key, newVal => {
				el.textContent = newVal
			})
		}
	}

	compileElement(el) {
		const attrs = el.attributes
		Array.from(attrs).forEach(attr => {
			let attrName = attr.name
			if (this.isDirective(attrName)) {
				attrName = attrName.substr(2)
				let key = attr.value
				this.update(el, key, attrName)
			}
		})
	}

	update(node, key, attrName) {
		let updateFn = this[attrName.split(':')[0] + 'Updater']
		updateFn && updateFn.call(this, node, this.vm[key] || attrName, key)
	}

	textUpdater(node, value, key) {
		node.textContent = value

		new Watcher(this.vm, key, newVal => {
			node.textContent = newVal
		})
	}

	modelUpdater(node, value, key) {
		node.value = value

		new Watcher(this.vm, key, newVal => {
			node.value = newVal
		})

		node.addEventListener('input', () => {
			this.vm[key] = node.value
		})
	}

	htmlUpdater(node, value, key) {
		node.innerHTML = value

		new Watcher(this.vm, key, newVal => {
			node.innerHTML = newVal
		})
	}

	onUpdater(node, value, key) {
		const event = value.substr(3)
		node.addEventListener(event, e => {
			new Function(key).apply(this.vm, e)
		})
	}

	isTextNode(node) {
		return node.nodeType === 3
	}

	isElementNode(node) {
		return node.nodeType === 1
	}

	isDirective(attrName) {
		return attrName.startsWith('v-')
	}
}
